targetScope = 'subscription'

/////////////////////////////////////////////////////////////////////////////
// TEMPLATE SETTINGS (PARAMETERS and VARIABLES)
/////////////////////////////////////////////////////////////////////////////

// Parameters Note:
// Use the file 'main.parameters.json' to set the parameter values.
// 'main.parameters.json', map the parameters to environment variables using the "${ENV_VAR_NAME}" notation.
// If you want to set the value of any of those variables, just run the following command: azd env set ENV_VAR_NAME value.
// The value of 'ENV_VAR_NAME' will be automatically fetched if you deploy the template using 'azd'.

// Templates Reference: https://learn.microsoft.com/en-us/azure/templates/

// Environment name. This is automatically set by the 'azd' tool.
@description('Environment name used as a tag for all resources. This is directly mapped to the azd-environment.')
param environmentName string = ''

// Location. This is automatically set by the 'azd' tool.
@description('Primary location for all resources.')
param location string = ''

// Resource group
@description('Name of the resource group where all resources will be created. When empty, the name is autogenerated.')
param resourceGroupName string = ''
var _resourceGroupName = !empty(resourceGroupName) ? resourceGroupName : 'rg-${environmentName}'

// Tag settings
// default required tags for azd deployment
var azdTags = { 'azd-env-name': environmentName }

@description('Key-value pairs of tags to assign to all resources. The default azd tags are automatically added.')
param deploymentTags object
var tags = union(azdTags, deploymentTags)

resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
  name: _resourceGroupName
  location: location
  tags: tags
}

// unique hash based on the subcription id, environment name and location. The hash is used to generate unique names for resources.
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))

// Disable load testing?
@description('Disable load testing? If yes it will not create load testing resource.')
@allowed([true, false])
param provisionLoadTesting bool = false
var _provisionLoadTesting = provisionLoadTesting

// Reuse preexisting resources settings

@description('Settings to define reusable resources.')
var _azureReuseConfigDefaults = {
  aoaiReuse: false
  existingAoaiResourceGroupName: ''
  existingAoaiName: ''
  appInsightsReuse: false
  existingAppInsightsResourceGroupName: ''
  existingAppInsightsName: ''
  appServicePlanReuse: false
  existingAppServicePlanResourceGroupName: ''
  existingAppServicePlanName: ''
  aiSearchReuse: false
  existingAiSearchResourceGroupName: ''
  existingAiSearchName: ''
  aiServicesReuse: false
  existingAiServicesResourceGroupName: ''
  existingAiServicesName: ''
  cosmosDbReuse: false
  existingCosmosDbResourceGroupName: ''
  existingCosmosDbAccountName: ''
  existingCosmosDbDatabaseName : ''
  keyVaultReuse: false
  existingKeyVaultResourceGroupName: ''
  existingKeyVaultName: ''
  storageReuse: false
  existingStorageResourceGroupName: ''
  existingStorageName: ''
  vnetReuse: false
  existingVnetResourceGroupName: ''
  existingVnetName: ''
  orchestratorFunctionAppReuse: false
  existingOrchestratorFunctionAppResourceGroupName: ''
  existingOrchestratorFunctionAppName: ''  
  dataIngestionFunctionAppReuse: false
  existingDataIngestionFunctionAppResourceGroupName: ''
  existingDataIngestionFunctionAppName: ''  
  appServiceReuse: false
  existingAppServiceName: ''
  existingAppServiceNameResourceGroupName: ''
  orchestratorFunctionAppStorageReuse: false
  existingOrchestratorFunctionAppStorageName: ''
  existingOrchestratorFunctionAppStorageResourceGroupName: ''
  dataIngestionFunctionAppStorageReuse: false
  existingDataIngestionFunctionAppStorageName: ''
  existingDataIngestionFunctionAppStorageResourceGroupName: ''  
}

param azureReuseConfig object = {} 
var _azureReuseConfig = union(_azureReuseConfigDefaults, {
    aoaiReuse: (empty(azureReuseConfig.aoaiReuse) ? _azureReuseConfigDefaults.aoaiReuse : toLower(azureReuseConfig.aoaiReuse) == 'true')
    existingAoaiResourceGroupName: (empty(azureReuseConfig.existingAoaiResourceGroupName) ? _azureReuseConfigDefaults.existingAoaiResourceGroupName : azureReuseConfig.existingAoaiResourceGroupName)
    existingAoaiName: (empty(azureReuseConfig.existingAoaiName) ? _azureReuseConfigDefaults.existingAoaiName : azureReuseConfig.existingAoaiName)
    aiServicesReuse: (empty(azureReuseConfig.aiServicesReuse) ? _azureReuseConfigDefaults.aiServicesReuse : toLower(azureReuseConfig.aiServicesReuse) == 'true')
    existingAiServicesResourceGroupName: (empty(azureReuseConfig.existingAiServicesResourceGroupName) ? _azureReuseConfigDefaults.existingAiServicesResourceGroupName : azureReuseConfig.existingAiServicesResourceGroupName)
    existingAiServicesName: (empty(azureReuseConfig.existingAiServicesName) ? _azureReuseConfigDefaults.existingAiServicesName : azureReuseConfig.existingAiServicesName)
    appInsightsReuse: (empty(azureReuseConfig.appInsightsReuse) ? _azureReuseConfigDefaults.appInsightsReuse : toLower(azureReuseConfig.appInsightsReuse) == 'true')
    existingAppInsightsResourceGroupName: (empty(azureReuseConfig.existingAppInsightsResourceGroupName) ? _azureReuseConfigDefaults.existingAppInsightsResourceGroupName : azureReuseConfig.existingAppInsightsResourceGroupName)
    existingAppInsightsName: (empty(azureReuseConfig.existingAppInsightsName) ? _azureReuseConfigDefaults.existingAppInsightsName : azureReuseConfig.existingAppInsightsName)
    appServicePlanReuse: (empty(azureReuseConfig.appServicePlanReuse) ? _azureReuseConfigDefaults.appServicePlanReuse : toLower(azureReuseConfig.appServicePlanReuse) == 'true')
    existingAppServicePlanResourceGroupName: (empty(azureReuseConfig.existingAppServicePlanResourceGroupName) ? _azureReuseConfigDefaults.existingAppServicePlanResourceGroupName : azureReuseConfig.existingAppServicePlanResourceGroupName)
    existingAppServicePlanName: (empty(azureReuseConfig.existingAppServicePlanName) ? _azureReuseConfigDefaults.existingAppServicePlanName : azureReuseConfig.existingAppServicePlanName)
    aiSearchReuse: (empty(azureReuseConfig.aiSearchReuse) ? _azureReuseConfigDefaults.aiSearchReuse : toLower(azureReuseConfig.aiSearchReuse) == 'true')
    existingAiSearchResourceGroupName: (empty(azureReuseConfig.existingAiSearchResourceGroupName) ? _azureReuseConfigDefaults.existingAiSearchResourceGroupName : azureReuseConfig.existingAiSearchResourceGroupName)
    existingAiSearchName: (empty(azureReuseConfig.existingAiSearchName) ? _azureReuseConfigDefaults.existingAiSearchName : azureReuseConfig.existingAiSearchName)
    cosmosDbReuse: (empty(azureReuseConfig.cosmosDbReuse) ? _azureReuseConfigDefaults.cosmosDbReuse : toLower(azureReuseConfig.cosmosDbReuse) == 'true')
    existingCosmosDbResourceGroupName: (empty(azureReuseConfig.existingCosmosDbResourceGroupName) ? _azureReuseConfigDefaults.existingCosmosDbResourceGroupName : azureReuseConfig.existingCosmosDbResourceGroupName)
    existingCosmosDbAccountName: (empty(azureReuseConfig.existingCosmosDbAccountName) ? _azureReuseConfigDefaults.existingCosmosDbAccountName : azureReuseConfig.existingCosmosDbAccountName)
    existingCosmosDbDatabaseName: (empty(azureReuseConfig.existingCosmosDbDatabaseName) ? _azureReuseConfigDefaults.existingCosmosDbDatabaseName : azureReuseConfig.existingCosmosDbDatabaseName)
    keyVaultReuse: (empty(azureReuseConfig.keyVaultReuse) ? _azureReuseConfigDefaults.keyVaultReuse : toLower(azureReuseConfig.keyVaultReuse) == 'true')
    existingKeyVaultResourceGroupName: (empty(azureReuseConfig.existingKeyVaultResourceGroupName) ? _azureReuseConfigDefaults.existingKeyVaultResourceGroupName : azureReuseConfig.existingKeyVaultResourceGroupName)
    existingKeyVaultName: (empty(azureReuseConfig.existingKeyVaultName) ? _azureReuseConfigDefaults.existingKeyVaultName : azureReuseConfig.existingKeyVaultName)
    storageReuse: (empty(azureReuseConfig.storageReuse) ? _azureReuseConfigDefaults.storageReuse : toLower(azureReuseConfig.storageReuse) == 'true')
    existingStorageResourceGroupName: (empty(azureReuseConfig.existingStorageResourceGroupName) ? _azureReuseConfigDefaults.existingStorageResourceGroupName : azureReuseConfig.existingStorageResourceGroupName)
    existingStorageName: (empty(azureReuseConfig.existingStorageName) ? _azureReuseConfigDefaults.existingStorageName : azureReuseConfig.existingStorageName)
    vnetReuse: (empty(azureReuseConfig.vnetReuse) ? _azureReuseConfigDefaults.vnetReuse : toLower(azureReuseConfig.vnetReuse) == 'true')
    existingVnetResourceGroupName: (empty(azureReuseConfig.existingVnetResourceGroupName) ? _azureReuseConfigDefaults.existingVnetResourceGroupName : azureReuseConfig.existingVnetResourceGroupName)
    existingVnetName: (empty(azureReuseConfig.existingVnetName) ? _azureReuseConfigDefaults.existingVnetName : azureReuseConfig.existingVnetName)
    orchestratorFunctionAppReuse: (empty(azureReuseConfig.orchestratorFunctionAppReuse) ? _azureReuseConfigDefaults.orchestratorFunctionAppReuse: toLower(azureReuseConfig.orchestratorFunctionAppReuse) == 'true')
    existingOrchestratorFunctionAppResourceGroupName: (empty(azureReuseConfig.existingOrchestratorFunctionAppResourceGroupName) ? _azureReuseConfigDefaults.existingOrchestratorFunctionAppResourceGroupName : azureReuseConfig.existingOrchestratorFunctionAppResourceGroupName)
    existingOrchestratorFunctionAppName: (empty(azureReuseConfig.existingOrchestratorFunctionAppName) ? _azureReuseConfigDefaults.existingOrchestratorFunctionAppName : azureReuseConfig.existingOrchestratorFunctionAppName)
    dataIngestionFunctionAppReuse: (empty(azureReuseConfig.dataIngestionFunctionAppReuse) ? _azureReuseConfigDefaults.dataIngestionFunctionAppReuse : toLower(azureReuseConfig.dataIngestionFunctionAppReuse) == 'true')
    existingDataIngestionFunctionAppResourceGroupName: (empty(azureReuseConfig.existingDataIngestionFunctionAppResourceGroupName) ? _azureReuseConfigDefaults.existingDataIngestionFunctionAppResourceGroupName : azureReuseConfig.existingDataIngestionFunctionAppResourceGroupName)
    existingDataIngestionFunctionAppName: (empty(azureReuseConfig.existingDataIngestionFunctionAppName) ? _azureReuseConfigDefaults.existingDataIngestionFunctionAppName : azureReuseConfig.existingDataIngestionFunctionAppName)
    appServiceReuse: (empty(azureReuseConfig.appServiceReuse) ? _azureReuseConfigDefaults.appServiceReuse : toLower(azureReuseConfig.appServiceReuse) == 'true')
    existingAppServiceName: (empty(azureReuseConfig.existingAppServiceName) ? _azureReuseConfigDefaults.existingAppServiceName : azureReuseConfig.existingAppServiceName)
    existingAppServiceNameResourceGroupName: (empty(azureReuseConfig.existingAppServiceNameResourceGroupName) ? _azureReuseConfigDefaults.existingAppServiceNameResourceGroupName : azureReuseConfig.existingAppServiceNameResourceGroupName)
    orchestratorFunctionAppStorageReuse: (empty(azureReuseConfig.orchestratorFunctionAppStorageReuse) ? _azureReuseConfigDefaults.orchestratorFunctionAppStorageReuse : toLower(azureReuseConfig.orchestratorFunctionAppStorageReuse) == 'true')
    existingOrchestratorFunctionAppStorageName: (empty(azureReuseConfig.existingOrchestratorFunctionAppStorageName) ? _azureReuseConfigDefaults.existingOrchestratorFunctionAppStorageName : azureReuseConfig.existingOrchestratorFunctionAppStorageName)
    existingOrchestratorFunctionAppStorageResourceGroupName: (empty(azureReuseConfig.existingOrchestratorFunctionAppStorageResourceGroupName) ? _azureReuseConfigDefaults.existingOrchestratorFunctionAppStorageResourceGroupName : azureReuseConfig.existingOrchestratorFunctionAppStorageResourceGroupName)
    dataIngestionFunctionAppStorageReuse: (empty(azureReuseConfig.dataIngestionFunctionAppStorageReuse) ? _azureReuseConfigDefaults.dataIngestionFunctionAppStorageReuse : toLower(azureReuseConfig.dataIngestionFunctionAppStorageReuse) == 'true')
    existingDataIngestionFunctionAppStorageName: (empty(azureReuseConfig.existingDataIngestionFunctionAppStorageName) ? _azureReuseConfigDefaults.existingDataIngestionFunctionAppStorageName : azureReuseConfig.existingDataIngestionFunctionAppStorageName)
    existingDataIngestionFunctionAppStorageResourceGroupName: (empty(azureReuseConfig.existingDataIngestionFunctionAppStorageResourceGroupName) ? _azureReuseConfigDefaults.existingDataIngestionFunctionAppStorageResourceGroupName : azureReuseConfig.existingDataIngestionFunctionAppStorageResourceGroupName)
  }
)


// Security settings

// Note on Password Generation and KeyVault Usage:
// The 'azd' tool can automatically generate a password and store it in KeyVault. Refer to the mapping in 'main.parameters.json' for 'vmUserInitialPassword'.
// If the referenced KeyVault already exists, 'azd' will retrieve the password from it. If not, 'azd' will generate a random password and store it in the newly created KeyVault.
// This template ensures the creation of the KeyVault and sets the output to align with the usage in 'main.parameters.json'.
@minLength(6)
@maxLength(72)
@description('Test vm gpt user password. Use strong password with letters and numbers. Needed only when choosing network isolation and create bastion option. If not creating with network isolation you can write anything. Password must be between 6-72 characters long and must satisfy at least 3 of password complexity requirements from the following: 1-Contains an uppercase character, 2-Contains a lowercase character, 3-Contains a numeric digit, 4-Contains a special character, 5- Control characters are not allowed.')
@secure()
param vmUserInitialPassword string

@description('Deploy VM? If yes it will create the virtual machine to access the network isolated environment in the zero trust configuration.')
@allowed([true, false])
param deployVM bool = true
var _deployVM = deployVM

// var _vmUserInitialPassword = vmUserInitialPassword

@description('Test vm gpt user name. Needed only when choosing network isolation and create bastion option. If not you can leave it blank.')
param vmUserName string = ''
var _vmUserName = !empty(vmUserName) ? vmUserName : 'gptrag'

// PricipalId that will have access to KeyVault secrets, this is automatically set by the 'azd' tool to the principal runing azd
@description('Id of the user or app to assign application roles')
param principalId string = ''

// Network settings

@description('Network isolation? If yes it will create the private endpoints.')
@allowed([true, false])
param networkIsolation bool = false
var _networkIsolation = networkIsolation

// VNet settings

@description('Virtual network name, you can leave as it is to generate a random name.')
param vnetName string = ''
var _vnetName = _azureReuseConfig.vnetReuse ? _azureReuseConfig.existingVnetName : !empty(vnetName) ? vnetName : 'aivnet0-${resourceToken}'

@description('Address space for the virtual network')
param vnetAddress string = ''
var _vnetAddress = !empty(vnetAddress) ? vnetAddress : '10.0.0.0/23'

@description('Name of the AI services subnet')
param aiSubnetName string = ''
var _aiSubnetName = !empty(aiSubnetName) ? aiSubnetName : 'ai-subnet'

@description('Address prefix for the AI services subnet')
param aiSubnetPrefix string = ''
var _aiSubnetPrefix = !empty(aiSubnetPrefix) ? aiSubnetPrefix : '10.0.0.0/26'

@description('Name of the Bastion subnet')
param bastionSubnetName string = ''
var _bastionSubnetName = !empty(bastionSubnetName) ? bastionSubnetName : 'AzureBastionSubnet'

@description('Address prefix for the Bastion subnet')
param bastionSubnetPrefix string = ''
var _bastionSubnetPrefix = !empty(bastionSubnetPrefix) ? bastionSubnetPrefix : '10.0.0.64/26'

@description('Name of the App Integration subnet')
param appIntSubnetName string = ''
var _appIntSubnetName = !empty(appIntSubnetName) ? appIntSubnetName : 'app-int-subnet'

@description('Address prefix for the App Integration subnet')
param appIntSubnetPrefix string = ''
var _appIntSubnetPrefix = !empty(appIntSubnetPrefix) ? appIntSubnetPrefix : '10.0.0.128/26'

@description('Name of the App Services subnet')
param appServicesSubnetName string = ''
var _appServicesSubnetName = !empty(appServicesSubnetName) ? appServicesSubnetName : 'app-services-subnet'

@description('Address prefix for the App Services subnet')
param appServicesSubnetPrefix string = ''
var _appServicesSubnetPrefix = !empty(appServicesSubnetPrefix) ? appServicesSubnetPrefix : '10.0.0.192/26'

@description('Name of the Database subnet')
param databaseSubnetName string = ''
var _databaseSubnetName = !empty(databaseSubnetName) ? databaseSubnetName : 'database-subnet'

@description('Address prefix for the Database subnet')
param databaseSubnetPrefix string = ''
var _databaseSubnetPrefix = !empty(databaseSubnetPrefix) ? databaseSubnetPrefix : '10.0.1.0/26'

// flag that indicates if we're reusing a vnet
var _vnetReuse = _azureReuseConfig.vnetReuse

// Database settings

var _azureDbConfigDefaults = {
  dbAccountName: 'dbgpt0-${resourceToken}'
  dbDatabaseName: 'db0-${resourceToken}'
  conversationContainerName: 'conversations'
  modelsContainerName: 'models'
}
param azureDbConfig object = {} 
var _azureDbConfig = union(_azureDbConfigDefaults, {
    dbAccountName: (empty(azureDbConfig.dbAccountName) ? _azureDbConfigDefaults.dbAccountName : azureDbConfig.dbAccountName)
    dbDatabaseName: (empty(azureDbConfig.dbDatabaseName) ? _azureDbConfigDefaults.dbDatabaseName : azureDbConfig.dbDatabaseName)
    conversationContainerName: (empty(azureDbConfig.conversationContainerName) ? _azureDbConfigDefaults.conversationContainerName : azureDbConfig.conversationContainerName)
    modelsContainerName: (empty(azureDbConfig.modelsContainerName) ? _azureDbConfigDefaults.modelsContainerName : azureDbConfig.modelsContainerName)
})
var _cosmosDbResourceGroupName = _azureReuseConfig.cosmosDbReuse ? _azureReuseConfig.existingCosmosDbResourceGroupName : _resourceGroupName

// Orchestrator settings

@description('Language used when orchestrator needs send error messages to the UX.')
param orchestratorMessagesLanguage string = ''
var _orchestratorMessagesLanguage = !empty(orchestratorMessagesLanguage) ? orchestratorMessagesLanguage : 'en'

//Frontend settings

@description('Language used for speech recognition in the frontend.')
// @allowed(['pt-BR', 'af-ZA', 'am-ET', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IL', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-OM', 'ar-PS', 'ar-QA', 'ar-SA', 'ar-SY', 'ar-TN', 'ar-YE', 'az-AZ', 'bg-BG', 'bn-IN', 'bs-BA', 'ca-ES', 'cs-CZ', 'cy-GB', 'da-DK', 'de-AT', 'de-CH', 'de-DE', 'el-GR', 'en-AU', 'en-CA', 'en-GB', 'en-GH', 'en-HK', 'en-IE', 'en-IN', 'en-KE', 'en-NG', 'en-NZ', 'en-PH', 'en-SG', 'en-TZ', 'en-US', 'en-ZA', 'es-AR', 'es-BO', 'es-CL', 'es-CO', 'es-CR', 'es-CU', 'es-DO', 'es-EC', 'es-ES', 'es-GQ', 'es-GT', 'es-HN', 'es-MX', 'es-NI', 'es-PA', 'es-PE', 'es-PR', 'es-PY', 'es-SV', 'es-US', 'es-UY', 'es-VE', 'et-EE', 'eu-ES', 'fa-IR', 'fi-FI', 'fil-PH', 'fr-BE', 'fr-CA', 'fr-CH', 'fr-FR', 'ga-IE', 'gl-ES', 'gu-IN', 'he-IL', 'hi-IN', 'hr-HR', 'hu-HU', 'hy-AM', 'id-ID', 'is-IS', 'it-CH', 'it-IT', 'ja-JP', 'jv-ID', 'ka-GE', 'kk-KZ', 'km-KH', 'kn-IN', 'ko-KR', 'lo-LA', 'lt-LT', 'lv-LV', 'mk-MK', 'ml-IN', 'mn-MN', 'mr-IN', 'ms-MY', 'mt-MT', 'my-MM', 'nb-NO', 'ne-NP', 'nl-BE', 'nl-NL', 'pl-PL', 'ps-AF', 'pt-PT', 'ro-RO', 'ru-RU', 'si-LK', 'sk-SK', 'sl-SI', 'so-SO', 'sq-AL', 'sr-RS', 'sv-SE', 'sw-KE', 'sw-TZ', 'ta-IN', 'te-IN', 'th-TH', 'tr-TR', 'uk-UA', 'uz-UZ', 'vi-VN', 'wuu-CN', 'yue-CN', 'zh-CN', 'zh-CN-shandong', 'zh-CN-sichuan', 'zh-HK', 'zh-TW', 'zu-ZA' ])
param speechRecognitionLanguage string = ''
var _speechRecognitionLanguage = !empty(speechRecognitionLanguage) ? speechRecognitionLanguage : 'en-US'

@description('Language used for speech synthesis in the frontend.')
// @allowed(['pt-BR', 'es-ES', 'es-MX','ar-EG', 'ar-SA', 'ca-ES', 'cs-CZ', 'da-DK', 'de-AT', 'de-CH', 'de-DE', 'en-AU', 'en-CA', 'en-GB', 'en-HK', 'en-IE', 'en-IN', 'en-US', 'es-ES', 'es-MX', 'fi-FI', 'fr-BE', 'fr-CA', 'fr-CH', 'fr-FR', 'hi-IN', 'hu-HU', 'id-ID', 'it-IT', 'ja-JP', 'ko-KR', 'nb-NO', 'nl-BE', 'nl-NL', 'pl-PL', 'pt-PT', 'ru-RU', 'sv-SE', 'th-TH', 'tr-TR', 'zh-CN', 'zh-HK', 'zh-TW'])
param speechSynthesisLanguage string = ''
var _speechSynthesisLanguage = !empty(speechSynthesisLanguage) ? speechSynthesisLanguage : 'en-US'

@description('Voice used for speech synthesis in the frontend.')
// @allowed([ 'pt-BR-FranciscaNeural', 'es-MX-BeatrizNeural', 'en-US-RyanMultilingualNeural', 'de-DE-AmalaNeural', 'fr-FR-DeniseNeural'])
param speechSynthesisVoiceName string = ''
var _speechSynthesisVoiceName = !empty(speechSynthesisVoiceName) ? speechSynthesisVoiceName : 'en-US-RyanMultilingualNeural'

// Function app settings

@description('Python runtime version in function apps')
// @allowed(['3.10', '3.11'])
param funcAppRuntimeVersion string = ''
var _funcAppRuntimeVersion = !empty(funcAppRuntimeVersion) ? funcAppRuntimeVersion : '3.11'

@description('Python runtime version in app service')
// @allowed(['3.10', '3.11', '3.12'])
param appServiceRuntimeVersion string = ''
var _appServiceRuntimeVersion = !empty(appServiceRuntimeVersion) ? appServiceRuntimeVersion : '3.12'

// Azure OpenAI settings

@description('GPT model used to answer user questions. Don\'t forget to check region availability.')
// @allowed([ 'gpt-35-turbo','gpt-35-turbo-16k', 'gpt-4', 'gpt-4-32k', 'gpt-4o', 'gpt-4o-mini' ])
param chatGptModelName string = ''
var _chatGptModelName = !empty(chatGptModelName) ? chatGptModelName : 'gpt-4o'

@description('GPT model deployment type.')
// @allowed([ 'Standard', 'Provisioned-Managed', 'Global-Standard'])
param chatGptModelDeploymentType string = ''
var _chatGptModelDeploymentType = !empty(chatGptModelDeploymentType) ? chatGptModelDeploymentType : 'Standard'

@description('GPT model version.')
// @allowed([ '0613', '1106', '1106-Preview', '0125-preview', 'turbo-2024-04-09', '2024-05-13'])
param chatGptModelVersion string = ''
var _chatGptModelVersion = !empty(chatGptModelVersion) ? chatGptModelVersion : '2024-05-13'

@description('GPT model deployment name.')
param chatGptDeploymentName string = ''
var _chatGptDeploymentName = !empty(chatGptDeploymentName) ? chatGptDeploymentName : 'chat'

@description('GPT model tokens per Minute Rate Limit (thousands).')
// @minValue(1)
// @maxValue(300)
param chatGptDeploymentCapacity int = 0
var _chatGptDeploymentCapacity = chatGptDeploymentCapacity != 0 ? chatGptDeploymentCapacity : 40

@description('Embeddings model used to generate vector embeddings. Don\'t forget to check region availability.')
// @allowed([ 'text-embedding-ada-002' ])
param embeddingsModelName string = ''
var _embeddingsModelName = !empty(embeddingsModelName) ? embeddingsModelName : 'text-embedding-ada-002'

@description('Embeddings model version.')
// @allowed([ '2' ])
param embeddingsModelVersion string = ''
var _embeddingsModelVersion = !empty(embeddingsModelVersion) ? embeddingsModelVersion : '2'

@description('Embeddings model deployment name.')
param embeddingsDeploymentName string = ''
var _embeddingsDeploymentName = !empty(embeddingsDeploymentName) ? embeddingsDeploymentName : 'text-embedding-ada-002'

@description('Embeddings model tokens per Minute Rate Limit (thousands).')
param embeddingsDeploymentCapacity int = 0
var _embeddingsDeploymentCapacity = embeddingsDeploymentCapacity != 0 ? embeddingsDeploymentCapacity : 40

@description('Azure OpenAI API version.')
param openaiApiVersion string = ''
var _openaiApiVersion = !empty(openaiApiVersion) ? openaiApiVersion : '2024-07-01-preview'

@description('Enables LLM monitoring to generate conversation metrics.')
@allowed([true, false])
param chatGptLlmMonitoring bool = false
var _chatGptLlmMonitoring = chatGptLlmMonitoring != null ? chatGptLlmMonitoring : true

// Document intelligence settings

var _docintApiVersion = (location == 'eastus' || location == 'westus2' || location == 'westeurope' || location == 'northcentralus') ? '2024-07-31-preview' : '2023-07-31'

// AI search settings

@description('Orchestrator supports the following retrieval approaches: term, vector, hybrid(term + vector search), or use oyd feature of Azure OpenAI.')
// @allowed(['term', 'vector', 'hybrid', 'oyd' ])
param retrievalApproach string = ''
var _retrievalApproach = !empty(retrievalApproach) ? retrievalApproach : 'hybrid'

@description('Analyzer language used by Azure search to analyze indexes text content.')
// @allowed(['standard', 'pt-Br.microsoft', 'es.microsoft', 'ar.microsoft', 'bn.microsoft', 'bg.microsoft', 'ca.microsoft', 'zh-Hans.microsoft', 'zh-Hant.microsoft', 'hr.microsoft', 'cs.microsoft', 'da.microsoft', 'nl.microsoft', 'en.microsoft', 'et.microsoft', 'fi.microsoft', 'fr.microsoft', 'de.microsoft', 'el.microsoft', 'gu.microsoft', 'he.microsoft', 'hi.microsoft', 'hu.microsoft', 'is.microsoft', 'id.microsoft', 'it.microsoft', 'ja.microsoft', 'kn.microsoft', 'ko.microsoft', 'lv.microsoft', 'lt.microsoft', 'ml.microsoft', 'ms.microsoft', 'mr.microsoft', 'nb.microsoft', 'pl.microsoft', 'pt-Pt.microsoft', 'pa.microsoft', 'ro.microsoft', 'ru.microsoft', 'sr-cyrillic.microsoft', 'sr-latin.microsoft', 'sk.microsoft', 'sl.microsoft', 'sv.microsoft', 'ta.microsoft', 'te.microsoft', 'th.microsoft', 'tr.microsoft', 'uk.microsoft', 'ur.microsoft', 'vi.microsoft' ])
param searchAnalyzerName string = ''
var _searchAnalyzerName = !empty(searchAnalyzerName) ? searchAnalyzerName : 'standard'

@description('Use semantic reranking on top of search results?.')
@allowed([true, false])
param useSemanticReranking bool = true
var _useSemanticReranking = useSemanticReranking != null ? useSemanticReranking : true

var _searchServiceSkuName = _networkIsolation?'standard2':'standard'

@description('Search index name.')
param searchIndex string = ''
var _searchIndex = !empty(searchIndex) ? searchIndex : 'ragindex'

// @allowed([ '2024-07-01', '2023-11-01', '2023-10-01-Preview', '2024-05-01-preview' ])
// Requires version 2023-10-01-Preview or higher for indexProjections and MIS authResourceId.
param searchApiVersion string = ''
var _searchApiVersion = !empty(searchApiVersion) ? searchApiVersion : '2024-07-01'

@description('Frequency of search reindexing. PT5M (5 min), PT1H (1 hour), P1D (1 day).')
// @allowed(['PT5M', 'PT1H', 'P1D'])
param searchIndexInterval string = ''
var _searchIndexInterval = !empty(searchIndexInterval) ? searchIndexInterval : 'PT1H'

@description('Use Search Service Managed Identity to Connect to data ingestion function?')
// @allowed([true, false])
param searchUseMIS bool = false
var _azureSearchUseMIS = searchUseMIS != null ? searchUseMIS : false

// chunking settings

@description('The number of tokens in each chunk.')
param chunkNumTokens string = ''
var _chunkNumTokens = !empty(chunkNumTokens) ? chunkNumTokens : '2048'

@description('The minimum chunk size below which chunks will be filtered.')
param chunkMinSize string = ''
var _chunkMinSize = !empty(chunkMinSize) ? chunkMinSize : '100'

@description('The number of tokens to overlap between chunks.')
param chunkTokenOverlap string = ''
var _chunkTokenOverlap = !empty(chunkTokenOverlap) ? chunkTokenOverlap : '200'

// Storage settings

@description('Name of the container where source documents will be stored.')
param storageContainerName string = ''
var _storageContainerName = !empty(storageContainerName) ? storageContainerName : 'documents'
var _storageImagesContainerName = '${_storageContainerName}-images'
var _storageNl2sqlContainerName = 'nl2sql'

@description('Storage Account Name. Use your own name convention or leave as it is to generate a random name.')
param storageAccountName string = ''
var _storageAccountName = _azureReuseConfig.storageReuse ? _azureReuseConfig.existingStorageName : !empty(storageAccountName) ? storageAccountName : 'strag0${resourceToken}'
var _storageAccountResourceGroupName = _azureReuseConfig.storageReuse ? _azureReuseConfig.existingStorageResourceGroupName : _resourceGroupName

// Resource name settings

// The name for each service can be set from environment variables which are mapped in main.parameters.json.
// If no maping to specific name is defined, a unique name is generated for each service based on the resourceToken created above.

@description('Key Vault Name. Use your own name convention or leave as it is to generate a random name.')
param keyVaultName string = ''
var _keyVaultName = _azureReuseConfig.keyVaultReuse ? _azureReuseConfig.existingKeyVaultName : !empty(keyVaultName) ? keyVaultName : 'kv0-${resourceToken}'
var _keyVaultResourceGroupName = _azureReuseConfig.keyVaultReuse ? _azureReuseConfig.existingKeyVaultResourceGroupName : _resourceGroupName

@description('OpenAI Service Name. Use your own name convention or leave as it is to generate a random name.')
param openAiServiceName string = ''
var _openAiServiceName = _azureReuseConfig.aoaiReuse ? _azureReuseConfig.existingAoaiName : !empty(openAiServiceName) ? openAiServiceName : 'oai0-${resourceToken}'
var _openAiResourceGroupName = _azureReuseConfig.aoaiReuse ? _azureReuseConfig.existingAoaiResourceGroupName : _resourceGroupName

@description('AI services multi-service name. Use your own name convention or leave as it is to generate a random name.')
param aiServicesName string = ''
var _aiServicesName = _azureReuseConfig.aiServicesReuse ? _azureReuseConfig.existingAiServicesName : !empty(aiServicesName) ? aiServicesName : 'ai0-${resourceToken}'
var _aiServicesResourceGroupName = _azureReuseConfig.aiServicesReuse ? _azureReuseConfig.existingAiServicesResourceGroupName : _resourceGroupName

@description('App Service Plan Name. Use your own name convention or leave as it is to generate a random name.')
param appServicePlanName string = ''
var _appServicePlanName = _azureReuseConfig.appServicePlanReuse ? _azureReuseConfig.existingAppServicePlanName : !empty(appServicePlanName) ? appServicePlanName : 'appplan0-${resourceToken}'

@description('App Insights Name. Use your own name convention or leave as it is to generate a random name.')
param appInsightsName string = ''
var _appInsightsName = _azureReuseConfig.appInsightsReuse ? _azureReuseConfig.existingAppInsightsName : !empty(appInsightsName) ? appInsightsName : 'appins0-${resourceToken}'
var _appInsightsResourceGroupName = _azureReuseConfig.appInsightsReuse ? _azureReuseConfig.existingAppInsightsResourceGroupName : _resourceGroupName

@description('Front-end App Service Name. Use your own name convention or leave as it is to generate a random name.')
param appServiceName string = ''
var _appServiceName = _azureReuseConfig.appServiceReuse ? _azureReuseConfig.existingAppServiceName : !empty(appServiceName) ? appServiceName : 'webgpt0-${resourceToken}'

@description('Load testing resource name. Use your own name convention or leave as it is to generate a random name.')
param loadTestingName string = ''
var _loadtestingName = !empty(loadTestingName) ? loadTestingName : 'loadtest0-${resourceToken}'

@description('Orchestrator Function Name. Use your own name convention or leave as it is to generate a random name.')
param orchestratorFunctionAppName string = ''
var _orchestratorFunctionAppName = _azureReuseConfig.orchestratorFunctionAppReuse ? _azureReuseConfig.existingOrchestratorFunctionAppName : !empty(orchestratorFunctionAppName) ? orchestratorFunctionAppName : 'fnorch0-${resourceToken}'
var _orchestratorFunctionAppResourceGroupName = _azureReuseConfig.orchestratorFunctionAppReuse ? _azureReuseConfig.existingOrchestratorFunctionAppResourceGroupName : _resourceGroupName

@description('Data Ingestion Function Name. Use your own name convention or leave as it is to generate a random name.')
param dataIngestionFunctionAppName string = ''
var _dataIngestionFunctionAppName = _azureReuseConfig.dataIngestionFunctionAppReuse ? _azureReuseConfig.existingDataIngestionFunctionAppName : !empty(dataIngestionFunctionAppName) ? dataIngestionFunctionAppName : 'fninges0-${resourceToken}'
var _dataIngestionFunctionAppResourceGroupName = _azureReuseConfig.dataIngestionFunctionAppReuse ? _azureReuseConfig.existingDataIngestionFunctionAppResourceGroupName : _resourceGroupName

@description('Search Service Name. Use your own name convention or leave as it is to generate a random name.')
param searchServiceName string = ''
var _searchServiceName = _azureReuseConfig.aiSearchReuse ? _azureReuseConfig.existingAiSearchName : !empty(searchServiceName) ? searchServiceName : 'search0-${resourceToken}'
var _searchResourceGroupName = _azureReuseConfig.aiSearchReuse ? _azureReuseConfig.existingAiSearchResourceGroupName : _resourceGroupName

@description('The name of the Azure Storage Account Private Endpoint. If left empty, a random name will be generated.')
param azureStorageAccountPe string = ''
var _azureStorageAccountPe = !empty(azureStorageAccountPe) ? azureStorageAccountPe : 'stragpe0-${resourceToken}'

@description('The name of the Azure Cosmos DB Private Endpoint. If left empty, a random name will be generated.')
param azureDbAccountPe string = ''
var _azureDbAccountPe = !empty(azureDbAccountPe) ? azureDbAccountPe : 'dbgptpe0-${resourceToken}'

@description('The name of the Azure Key Vault Private Endpoint. If left empty, a random name will be generated.')
param azureKeyvaultPe string = ''
var _azureKeyvaultPe = !empty(azureKeyvaultPe) ? azureKeyvaultPe : 'kvpe0-${resourceToken}'

@description('The name of the Azure Orchestrator Private Endpoint. If left empty, a random name will be generated.')
param azureOrchestratorPe string = ''
var _azureOrchestratorPe = !empty(azureOrchestratorPe) ? azureOrchestratorPe : 'orchestratorPe-${resourceToken}'

@description('The name of the Azure Frontend Private Endpoint. If left empty, a random name will be generated.')
param azureFrontendPe string = ''
var _azureFrontendPe = !empty(azureFrontendPe) ? azureFrontendPe : 'frontendPe-${resourceToken}'

@description('The name of the Azure Data Ingestion Private Endpoint. If left empty, a random name will be generated.')
param azureDataIngestionPe string = ''
var _azureDataIngestionPe = !empty(azureDataIngestionPe) ? azureDataIngestionPe : 'ingestionPe-${resourceToken}'

@description('The name of the Azure AI Services Private Endpoint. If left empty, a random name will be generated.')
param azureAiServicesPe string = ''
var _azureAiServicesPe = !empty(azureAiServicesPe) ? azureAiServicesPe : 'aiServicesPe-${resourceToken}'

@description('The name of the Azure OpenAI Private Endpoint. If left empty, a random name will be generated.')
param azureOpenAiPe string = ''
var _azureOpenAiPe = !empty(azureOpenAiPe) ? azureOpenAiPe : 'openAiPe-${resourceToken}'

@description('The name of the Azure Search Private Endpoint. If left empty, a random name will be generated.')
param azureSearchPe string = ''
var _azureSearchPe = !empty(azureSearchPe) ? azureSearchPe : 'searchPe-${resourceToken}'

@description('The name of the VM Key Vault Secret. If left empty, a random name will be generated.')
param vmKeyVaultSecName string = ''
var _vmKeyVaultSecName = !empty(vmKeyVaultSecName) ? vmKeyVaultSecName : 'vmUserInitialPassword'

@description('The name of the Zero Trust VM. If left empty, a random name will be generated.')
param ztVmName string = ''
var _ztVmName = !empty(ztVmName) ? ztVmName : 'testvm-${resourceToken}'

@description('The name of the Bastion Key Vault. If left empty, a random name will be generated.')
param bastionKvName string = ''
var _bastionKvName = !empty(bastionKvName) ? bastionKvName : 'bastionkv-${resourceToken}'

var _orchestratorEndpoint = 'https://${_orchestratorFunctionAppName}.azurewebsites.net/api/orc'

/////////////////////////////////////////////////////////////////////////////
// TEMPLATE MODULES
/////////////////////////////////////////////////////////////////////////////

// Networking

module vnet './core/network/vnet.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'virtual-network'
  scope: resourceGroup
  params: {
    location: location
    vnetName: _vnetName
    vnetReuse: _vnetReuse
    existingVnetResourceGroupName: _azureReuseConfig.existingVnetResourceGroupName
    tags: tags
    vnetAddress: _vnetAddress
    appServicePlanId: appServicePlan.outputs.id
    appServicePlanName: appServicePlan.outputs.name
    aiSubnetName: _aiSubnetName
    aiSubnetPrefix: _aiSubnetPrefix
    appIntSubnetName: _appIntSubnetName
    appIntSubnetPrefix: _appIntSubnetPrefix
    appServicesSubnetName: _appServicesSubnetName
    appServicesSubnetPrefix: _appServicesSubnetPrefix
    databaseSubnetName: _databaseSubnetName
    databaseSubnetPrefix: _databaseSubnetPrefix
    bastionSubnetName: _bastionSubnetName
    bastionSubnetPrefix: _bastionSubnetPrefix
  }
}

module blobDnsZone './core/network/private-dns-zones.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'blob-dnzones'
  scope: resourceGroup
  params: {
    dnsZoneName: 'privatelink.blob.core.windows.net' 
    tags: tags
    virtualNetworkName: _networkIsolation?vnet.outputs.name:''
  }
}

module documentsDnsZone './core/network/private-dns-zones.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'documents-dnzones'
  scope: resourceGroup
  params: {
    dnsZoneName: 'privatelink.documents.azure.com' 
    tags: tags
    virtualNetworkName: _networkIsolation?vnet.outputs.name:''
  }
}

module vaultDnsZone './core/network/private-dns-zones.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'vault-dnzones'
  scope: resourceGroup
  params: {
    dnsZoneName: 'privatelink.vaultcore.azure.net' 
    tags: tags
    virtualNetworkName: _networkIsolation?vnet.outputs.name:''
  }
}

module websitesDnsZone './core/network/private-dns-zones.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'websites-dnzones'
  scope: resourceGroup
  params: {
    dnsZoneName: 'privatelink.azurewebsites.net' 
    tags: tags
    virtualNetworkName: _networkIsolation?vnet.outputs.name:''
  }
}

module aiservicesDnsZone './core/network/private-dns-zones.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'aiservices-dnzones'
  scope: resourceGroup
  params: {
    dnsZoneName: 'privatelink.cognitiveservices.azure.com' 
    tags: tags
    virtualNetworkName: _networkIsolation?vnet.outputs.name:''
  }
}

module openaiDnsZone './core/network/private-dns-zones.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'openai-dnzones'
  scope: resourceGroup
  params: {
    dnsZoneName: 'privatelink.openai.azure.com' 
    tags: tags
    virtualNetworkName: _networkIsolation?vnet.outputs.name:''
  }
}

module searchDnsZone './core/network/private-dns-zones.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'searchs-dnzones'
  scope: resourceGroup
  params: {
    dnsZoneName: 'privatelink.search.windows.net' 
    tags: tags
    virtualNetworkName: _networkIsolation?vnet.outputs.name:''
  }
}

module testvm './core/vm/dsvm.bicep' = if (_networkIsolation && !_vnetReuse && _deployVM)  {
  name: 'testvm'
  scope: resourceGroup
  params: {
    location: location
    name: _ztVmName
    tags: tags
    subnetId:  _networkIsolation?vnet.outputs.aiSubId:''
    bastionSubId: _networkIsolation?vnet.outputs.bastionSubId:''
    vmUserPassword: vmUserInitialPassword
    vmUserName: _vmUserName
    keyVaultName: _bastionKvName
    // this is the named of the secret to store the vm password in keyvault. It matches what is used on main.parameters.json
    vmUserPasswordKey: _vmKeyVaultSecName
    principalId: principalId
  }
}

// Storage

module storage './core/storage/storage-account.bicep' = {
  name: 'storage'
  scope: resourceGroup
  params: {
    name: _storageAccountName
    location: location
    storageReuse: _azureReuseConfig.storageReuse
    existingStorageResourceGroupName: _azureReuseConfig.existingStorageResourceGroupName
    tags: tags
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'
    allowBlobPublicAccess: false // Disable anonymous access
    containers: [
      { name: _storageContainerName, publicAccess: 'None' }
      { name: _storageImagesContainerName, publicAccess: 'None' }
      { name: _storageNl2sqlContainerName, publicAccess: 'None' }      
    ]
    deleteRetentionPolicy: {
      enabled: true
      days: 7
    }
  }  
}

module storagepe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'storagepe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureStorageAccountPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.aiSubId:''
    serviceId: storage.outputs.id
    groupIds: ['blob']
    dnsZoneId: _networkIsolation?blobDnsZone.outputs.id:''
  }
}

// Database

module cosmosAccount './core/db/cosmos.bicep' = {
  name: 'cosmosaccount'
  scope: resourceGroup
  params: {
    accountName: _azureDbConfig.dbAccountName
    cosmosDbReuse: _azureReuseConfig.cosmosDbReuse
    existingCosmosDbResourceGroupName: _azureReuseConfig.existingCosmosDbResourceGroupName 
    existingCosmosDbAccountName: _azureReuseConfig.existingCosmosDbAccountName
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'
    location: location
    conversationContainerName:  _azureDbConfig.conversationContainerName
    modelsContainerName: _azureDbConfig.modelsContainerName   
    databaseName: _azureDbConfig.dbDatabaseName
    tags: tags
    secretName: 'azureDBkey'
    keyVaultName: keyVault.outputs.name    
  }
}

module cosmospe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'cosmospe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureDbAccountPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.databaseSubId:''
    serviceId: cosmosAccount.outputs.id
    groupIds: ['Sql']
    dnsZoneId: _networkIsolation?documentsDnsZone.outputs.id:''
  }
}

// Security

module keyVault './core/security/keyvault.bicep' = {
  name: 'keyvault'
  scope: resourceGroup
  params: {
    name: _keyVaultName
    location: location
    keyVaultReuse : _azureReuseConfig.keyVaultReuse
    existingKeyVaultResourceGroupName : _azureReuseConfig.existingKeyVaultResourceGroupName
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'
    tags: tags
    principalId: principalId
    // this is the named of the secret to store the vm password in keyvault. It matches what is used on main.parameters.json
    // vmUserPasswordKey: _vmKeyVaultSecName
    // vmUserPassword: _vmUserInitialPassword
  }
}

module keyvaultpe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'keyvaultpe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureKeyvaultPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.aiSubId:''
    serviceId: keyVault.outputs.id
    groupIds: ['Vault']
    dnsZoneId: _networkIsolation?vaultDnsZone.outputs.id:''
  }
}

// App Service Plan
module appServicePlan './core/host/appserviceplan.bicep' =  {
  name: 'appserviceplan'
  scope: resourceGroup
  params: {
    name: _appServicePlanName
    location: location
    appServicePlanReuse : _azureReuseConfig.appServicePlanReuse
    existingAppServicePlanResourceGroupName : _azureReuseConfig.existingAppServicePlanResourceGroupName
    tags: tags
    sku: {
      name: 'P0v3'
      capacity: 1
    }
    kind: 'linux'
  }
}

// App Insights
module appInsights './core/host/appinsights.bicep' = {
  name: 'appinsights'
  scope: resourceGroup
  params: {
    applicationInsightsName: _appInsightsName
    appInsightsLocation: location
    appInsightsReuse: _azureReuseConfig.appInsightsReuse
    existingAppInsightsResourceGroupName: _azureReuseConfig.existingAppInsightsResourceGroupName
  }
}

// Orchestrator Function App
module orchestrator './core/host/functions.bicep' =  {
  name: 'orchestrator'
  scope: resourceGroup
  params: {
    name: _orchestratorFunctionAppName
    functionAppResourceGroupName: _orchestratorFunctionAppResourceGroupName
    functionAppReuse: _azureReuseConfig.orchestratorFunctionAppReuse
    location: location
    networkIsolation: _networkIsolation
    vnetName: (_networkIsolation && !_vnetReuse)?vnet.outputs.name:''
    subnetId: (_networkIsolation && !_vnetReuse)?vnet.outputs.appIntSubId:''
    tags: union(tags, { 'azd-service-name': 'orchestrator' })
    identityType: 'SystemAssigned'
    keyVaultName: keyVault.outputs.name
    keyVaultResourceGroupName: _keyVaultResourceGroupName
    applicationInsightsName: appInsights.outputs.name
    applicationInsightsResourceGroupName: _appInsightsResourceGroupName
    appServicePlanId: appServicePlan.outputs.id
    runtimeName: 'python'
    runtimeVersion: _funcAppRuntimeVersion
    storageAccountName: orchestratorStorage.outputs.name 
    storageResourceGroupName: _orchestratorFunctionAppResourceGroupName // creates storage account in the same resource group as the function app
    numberOfWorkers: 2
    functionAppScaleLimit: 2
    minimumElasticInstanceCount: 1
    allowedOrigins: [ '*' ]       
    appSettings: [
      {
        name: 'AZURE_DB_ID'
        value: _azureDbConfig.dbAccountName
      }
      {
        name: 'AZURE_DB_NAME'
        value: _azureDbConfig.dbDatabaseName
      }
      {
        name: 'AZURE_DB_CONVERSATIONS_CONTAINER_NAME'
        value: _azureDbConfig.conversationContainerName
      }
      {
        name: 'AZURE_DB_MODELS_CONTAINER_NAME'
        value: _azureDbConfig.modelsContainerName
      }
      {
        name: 'AZURE_KEY_VAULT_NAME'
        value: keyVault.outputs.name
      }
      {
        name: 'AZURE_SEARCH_SERVICE'
        value: _searchServiceName
      }
      {
        name: 'AZURE_SEARCH_INDEX'
        value: _searchIndex
      }
      {
        name: 'AZURE_SEARCH_APPROACH'
        value: _retrievalApproach
      }
      {
        name: 'AZURE_SEARCH_USE_SEMANTIC'
        value: _useSemanticReranking
      }
      {
        name: 'AZURE_SEARCH_API_VERSION'
        value: _searchApiVersion
      }
      {
        name: 'AZURE_OPENAI_RESOURCE'
        value: _openAiServiceName
      }
      {
        name: 'AZURE_OPENAI_CHATGPT_MODEL'
        value: _chatGptModelName
      }
      {
        name: 'AZURE_OPENAI_CHATGPT_DEPLOYMENT'
        value: _chatGptDeploymentName
      }
      {
        name: 'AZURE_OPENAI_CHATGPT_LLM_MONITORING'
        value: _chatGptLlmMonitoring
      }
      {
        name: 'AZURE_OPENAI_API_VERSION'
        value: _openaiApiVersion
      }
      {
        name: 'AZURE_OPENAI_LOAD_BALANCING'
        value: 'false'
      }
      {
        name: 'AZURE_OPENAI_EMBEDDING_MODEL'
        value: _embeddingsModelName
      }
      {
        name: 'AZURE_OPENAI_EMBEDDING_DEPLOYMENT'
        value: _embeddingsDeploymentName
      }
      {
        name: 'AZURE_OPENAI_STREAM'
        value: 'false'
      }
      {
        name: 'ORCHESTRATOR_MESSAGES_LANGUAGE'
        value: _orchestratorMessagesLanguage
      }
      {
        name: 'ENABLE_ORYX_BUILD'
        value: 'true'
      }
      {
        name: 'BING_SEARCH_TOP_K'
        value: '3'
      }
      {
        name: 'BING_RETRIEVAL'
        value: 'false'
      }
      {
        name: 'BING_SEARCH_MAX_TOKENS'
        value: '1000'
      }
      {
        name: 'SQL_RETRIEVAL'
        value: 'false'
      }
      {
        name: 'SQL_TOP_K'
        value: '3'
      }
      {
        name: 'SQL_MAX_TOKENS'
        value: '1000'
      }
      {
        name: 'TERADATA_TOP_K'
        value: '3'
      }
      {
        name: 'TERADATA_RETRIEVAL'
        value: 'false'
      }
      {
        name: 'TERADATA_MAX_TOKENS'
        value: '1000'
      }
      {
        name: 'RETRIEVAL_PRIORITY'
        value: 'search'
      }
      {
        name: 'SCM_DO_BUILD_DURING_DEPLOYMENT'
        value: 'true'
      }
      {
        name: 'LOGLEVEL'
        value: 'INFO'
      }
    ]
  }
  dependsOn: [
    appServicePlan
  ]
}

// Orchestrator Storage Account

module orchestratorStorage './core/storage/function-storage-account.bicep' = {
  name: 'orchestratorstorage'
  scope: resourceGroup
  params: {
    name: '${_storageAccountName}orc'
    location: location
    tags: tags
    containers: [{name: 'deploymentpackage'}]
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'    
  }
}

module orchestratorStoragepe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'orchestratorstoragepe'
  scope: resourceGroup
  params: {
    location: location
    name: '${_azureStorageAccountPe}orc'
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.appServicesSubId:''
    serviceId: orchestratorStorage.outputs.id
    groupIds: ['blob']
    dnsZoneId: _networkIsolation?blobDnsZone.outputs.id:''
  }
}

module orchestratorPe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'orchestratorPe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureOrchestratorPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.appServicesSubId:''
    serviceId: orchestrator.outputs.id
    groupIds: ['sites']
    dnsZoneId: _networkIsolation?websitesDnsZone.outputs.id:''
  }
}

module orchestratorStorageAccountStorageAccess './core/security/blobstorage-dataowner-access.bicep' = {
  name: 'orchestratorstorageroleassignment'
  scope: resourceGroup
  params: {
    resourceName: orchestratorStorage.outputs.name
    principalID: orchestrator.outputs.identityPrincipalId
  }
}

module orchestratorStorageAccess './core/security/blobstorage-reader-access.bicep' = {
  name: 'orchestrator-blobstorage-access'
  scope: az.resourceGroup(_storageAccountResourceGroupName)
  params: {
    resourceName: storage.outputs.name
    principalId: orchestrator.outputs.identityPrincipalId
  }
}

module orchestratorKeyVaultAccess './core/security/keyvault-access.bicep' =  {
  name: 'orchestrator-keyvault-access'
  scope: az.resourceGroup(_keyVaultResourceGroupName)
  params: {
    resourceName: keyVault.outputs.name
    principalId: orchestrator.outputs.identityPrincipalId
  }
} 

module orchestratorCosmosAccess './core/security/cosmos-access.bicep' =  {
  name: 'orchestrator-cosmos-access'
  scope: az.resourceGroup(_cosmosDbResourceGroupName)
  params: {
    principalId: orchestrator.outputs.identityPrincipalId
    accountName: cosmosAccount.outputs.name
    resourceGroupName: _cosmosDbResourceGroupName
  }
} 

module orchestratorOaiAccess './core/security/openai-access.bicep' = {
  name: 'orchestrator-openai-access'
  scope: az.resourceGroup(_openAiResourceGroupName)
  params: {
    principalId: orchestrator.outputs.identityPrincipalId
    resourceName: openAi.outputs.name
  }
} 

module orchestratorSearchAccess './core/security/search-index-contributor-access.bicep' = {
  name: 'orchestrator-search-access'
  scope: az.resourceGroup(_searchResourceGroupName)
  params: {
    principalId: orchestrator.outputs.identityPrincipalId
    resourceName: searchService.outputs.name
  }
} 

// FrontEnd App

module frontEnd  'core/host/appservice.bicep' = {
  name: 'frontend'
  scope: resourceGroup
  params: {
    name: _appServiceName
    applicationInsightsName: _azureReuseConfig.appInsightsReuse?_azureReuseConfig.existingAppInsightsName:_appInsightsName
    applicationInsightsResourceGroupName: _azureReuseConfig.appInsightsReuse?_azureReuseConfig.existingAppInsightsResourceGroupName:_resourceGroupName  
    appServiceReuse: _azureReuseConfig.appServiceReuse
    existingAppServiceResourceGroupName: _azureReuseConfig.existingAppServiceNameResourceGroupName
    networkIsolation: (_networkIsolation && !_vnetReuse)
    vnetName: (_networkIsolation && !_vnetReuse)?vnet.outputs.name:''
    subnetId: (_networkIsolation && !_vnetReuse)?vnet.outputs.appIntSubId:''
    appCommandLine: 'python ./app.py'
    location: location
    tags: union(tags, { 'azd-service-name': 'frontend' })
    appServicePlanId: appServicePlan.outputs.id
    runtimeName: 'python'
    runtimeVersion: _appServiceRuntimeVersion
    scmDoBuildDuringDeployment: true
    basicPublishingCredentials: _networkIsolation?true:false
    appSettings: [
      {
        name: 'SPEECH_SYNTHESIS_VOICE_NAME'
        value: _speechSynthesisVoiceName
      }
      {
        name: 'SPEECH_SYNTHESIS_LANGUAGE'
        value: _speechSynthesisLanguage
      }      
      {
        name: 'SPEECH_RECOGNITION_LANGUAGE'
        value: _speechRecognitionLanguage
      }
      {
        name: 'SPEECH_REGION'
        value: location
      }
      {
        name: 'ORCHESTRATOR_ENDPOINT'
        value: _orchestratorEndpoint
      }
      {
        name: 'AZURE_SUBSCRIPTION_ID'
        value: subscription().subscriptionId
      }
      {
        name: 'AZURE_RESOURCE_GROUP_NAME'
        value: _resourceGroupName
      }
      {
        name: 'AZURE_ORCHESTRATOR_FUNC_NAME'
        value: _orchestratorFunctionAppName
      }            
      {
        name: 'AZURE_KEY_VAULT_ENDPOINT'
        value: keyVault.outputs.endpoint
      }
      {
        name: 'AZURE_KEY_VAULT_NAME'
        value: keyVault.outputs.name
      }
      {
        name: 'STORAGE_ACCOUNT'
        value: _storageAccountName
      } 
      {
        name: 'LOGLEVEL'
        value: 'INFO'
      } 
    ]
  }
}

module frontendPe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'frontendPe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureFrontendPe
    tags: tags
    subnetId: _networkIsolation ? vnet.outputs.appServicesSubId : ''
    serviceId: frontEnd.outputs.id
    groupIds: ['sites']
    dnsZoneId: _networkIsolation?websitesDnsZone.outputs.id:''
  }
}

module appserviceKeyVaultAccess './core/security/keyvault-access.bicep' = {
  name: 'appservice-keyvault-access'
  scope: az.resourceGroup(_keyVaultResourceGroupName)
  params: {
    resourceName: keyVault.outputs.name
    principalId: frontEnd.outputs.identityPrincipalId
  }
}

module appserviceStorageAccountAccess './core/security/blobstorage-reader-access.bicep' = {
  name: 'appservice-blobstorage-access'
  scope: az.resourceGroup(_storageAccountResourceGroupName)
  params: {
    resourceName: storage.outputs.name
    principalId: frontEnd.outputs.identityPrincipalId
  }
}

module appserviceOrchestratorAccess './core/security/functions-access.bicep' = {
  name: 'appservice-function-access'
  scope: az.resourceGroup(_orchestratorFunctionAppResourceGroupName)
  params: {
    resourceName: orchestrator.outputs.name
    principalId: frontEnd.outputs.identityPrincipalId
  }
}

module appserviceAIAccess './core/security/aiservices-access.bicep' = {
  name: 'appservice-ai-access'
  scope: az.resourceGroup(_aiServicesResourceGroupName)
  params: {
    principalId: frontEnd.outputs.identityPrincipalId
    resourceName: aiServices.outputs.name
  }
} 


module dataIngestion './core/host/functions.bicep' = {
  name: 'dataIngestion'
  scope: resourceGroup
  params: {
    name: _dataIngestionFunctionAppName
    functionAppResourceGroupName: _dataIngestionFunctionAppResourceGroupName
    functionAppReuse: _azureReuseConfig.dataIngestionFunctionAppReuse
    location: location
    networkIsolation: _networkIsolation
    vnetName: (_networkIsolation && !_vnetReuse)?vnet.outputs.name:''
    subnetId: (_networkIsolation && !_vnetReuse)?vnet.outputs.appIntSubId:'' 
    tags: union(tags, { 'azd-service-name': 'dataIngest' })
    identityType: 'SystemAssigned'
    // identityId: identityId
    keyVaultName: keyVault.outputs.name
    keyVaultResourceGroupName: _keyVaultResourceGroupName
    applicationInsightsName: appInsights.outputs.name
    applicationInsightsResourceGroupName: _appInsightsResourceGroupName
    appServicePlanId: appServicePlan.outputs.id
    runtimeName: 'python'
    runtimeVersion: _funcAppRuntimeVersion
    storageAccountName: dataIngestionStorage.outputs.name
    storageResourceGroupName: _dataIngestionFunctionAppResourceGroupName // creates storage account in the same resource group as the function app
    numberOfWorkers: 2
    functionAppScaleLimit: 2
    minimumElasticInstanceCount: 1
    allowedOrigins: [ '*' ]       
    appSettings: [
      // {
      //   name: 'AzureWebJobsStorage__clientId'
      //   value: identityClientId
      // }
      // {
      //   name: 'APPLICATIONINSIGHTS_AUTHENTICATION_STRING'
      //   value: applicationInsightsIdentity
      // }      
      {
        name: 'DOCINT_API_VERSION'
        value: _docintApiVersion
      }
      {
        name: 'AZURE_KEY_VAULT_NAME'
        value: keyVault.outputs.name
      }
      {
        name: 'FUNCTION_APP_NAME'
        value: _dataIngestionFunctionAppName
      }
      {
        name: 'SEARCH_INDEX_NAME'
        value: _searchIndex
      }
      {
        name: 'SEARCH_ANALYZER_NAME'
        value: _searchAnalyzerName
      }
      {
        name: 'SEARCH_API_VERSION'
        value: _searchApiVersion
      }
      {
        name: 'SEARCH_INDEX_INTERVAL'
        value: _searchIndexInterval
      }
      {
        name: 'STORAGE_ACCOUNT_NAME'
        value: _storageAccountName
      }
      {
        name: 'STORAGE_CONTAINER'
        value: _storageContainerName
      }
      {
        name: 'STORAGE_CONTAINER_IMAGES'
        value: _storageImagesContainerName
      }
      {
        name: 'AZURE_FORMREC_SERVICE'
        value: _aiServicesName
      }
      {
        name: 'AZURE_OPENAI_API_VERSION'
        value: _openaiApiVersion
      }
      {
        name: 'AZURE_SEARCH_APPROACH'
        value: _retrievalApproach
      }
      {
        name: 'AZURE_SEARCH_SERVICE'
        value: _searchServiceName
      }            
      {
        name: 'AZURE_OPENAI_SERVICE_NAME'
        value: _openAiServiceName
      }
      {
        name: 'AZURE_OPENAI_EMBEDDING_DEPLOYMENT'
        value: _embeddingsDeploymentName
      }
      {
        name: 'AZURE_OPENAI_CHATGPT_DEPLOYMENT'
        value: _chatGptDeploymentName
      }
      {
        name: 'NUM_TOKENS'
        value: _chunkNumTokens
      }
      {
        name: 'MIN_CHUNK_SIZE'
        value: _chunkMinSize
      }
      {
        name: 'TOKEN_OVERLAP'
        value: _chunkTokenOverlap
      }
      {
        name: 'NETWORK_ISOLATION'
        value: _networkIsolation
      }
      {
        name: 'ENABLE_ORYX_BUILD'
        value: 'true'
      }
      {
        name: 'SCM_DO_BUILD_DURING_DEPLOYMENT'
        value: 'true'
      }
      {
        name: 'AzureWebJobsFeatureFlags'
        value: 'EnableWorkerIndexing'
      }
      {
        name: 'LOGLEVEL'
        value: 'INFO'
      }
    ]        
  }
}


// Data Ingestion Storage Account

module dataIngestionStorage './core/storage/function-storage-account.bicep' = {
  name: 'dataingestionstorage'
  scope: resourceGroup
  params: {
    name: '${_storageAccountName}ing'
    location: location
    tags: tags
    containers: [{name: 'deploymentpackage'}]
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'
  }
}

module dataIngestionStoragepe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'dataingestionstoragepe'
  scope: resourceGroup
  params: {
    location: location
    name: '${_azureStorageAccountPe}ing'
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.appServicesSubId:''
    serviceId: dataIngestionStorage.outputs.id
    groupIds: ['blob']
    dnsZoneId: _networkIsolation?blobDnsZone.outputs.id:''
  }
}

module dataIngestionStorageAccountStorageAccess './core/security/blobstorage-dataowner-access.bicep' = {
  name: 'dataingestionstorageroleassignment'
  scope: resourceGroup
  params: {
    resourceName: dataIngestionStorage.outputs.name
    principalID: dataIngestion.outputs.identityPrincipalId
  }
}

module dataIngestionKeyVaultAccess './core/security/keyvault-access.bicep' = {
  name: 'data-ingestion-keyvault-access'
  scope: az.resourceGroup(_keyVaultResourceGroupName)
  params: {
    resourceName: keyVault.outputs.name
    principalId: dataIngestion.outputs.identityPrincipalId
  }
}

module dataIngestionBlobStorageAccess './core/security/blobstorage-contributor-access.bicep' = {
  name: 'data-ingestion-blobstorage-access'
  scope: az.resourceGroup(_storageAccountResourceGroupName)
  params: {
    resourceName: storage.outputs.name
    principalId: dataIngestion.outputs.identityPrincipalId
  }
}

module dataIngestionOaiAccess './core/security/openai-access.bicep' = {
  name: 'dataingestion-openai-access'
  scope: az.resourceGroup(_openAiResourceGroupName)
  params: {
    principalId: dataIngestion.outputs.identityPrincipalId
    resourceName: openAi.outputs.name
  }
} 

module dataIngestionAIAccess './core/security/aiservices-access.bicep' = {
  name: 'dataingestion-ai-access'
  scope: az.resourceGroup(_aiServicesResourceGroupName)
  params: {
    principalId: dataIngestion.outputs.identityPrincipalId
    resourceName: aiServices.outputs.name
  }
} 

module dataIngestionSearchAccess './core/security/search-index-contributor-access.bicep' = {
  name: 'data-ingestion-search-access'
  scope: az.resourceGroup(_searchResourceGroupName)
  params: {
    principalId: dataIngestion.outputs.identityPrincipalId
    resourceName: searchService.outputs.name
  }
} 

module ingestionPe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'ingestionPe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureDataIngestionPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.appServicesSubId:''
    serviceId: dataIngestion.outputs.id
    groupIds: ['sites']
    dnsZoneId: _networkIsolation?websitesDnsZone.outputs.id:''
  }
}

// AI Services

module aiServices 'core/ai/aiservices.bicep' = {
  name: 'AiServices'
  scope: resourceGroup
  params: {
    name: _aiServicesName
    location: location
    aiServicesDeploy: true
    aiServicesReuse : _azureReuseConfig.aiServicesReuse
    existingAiServicesResourceGroupName : _azureReuseConfig.existingAiServicesResourceGroupName
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'
    kind: 'CognitiveServices'
    tags: tags
    sku: {
      name: 'S0'
    }
    secretsNames: { 
      secretName01: 'formRecKey' 
      secretName02: 'speechKey'
    }
    keyVaultName: keyVault.outputs.name
  }
}

module aiServicesPe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'aiServicesPe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureAiServicesPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.aiSubId:''
    serviceId: aiServices.outputs.id
    groupIds: ['account']
    dnsZoneId: _networkIsolation?aiservicesDnsZone.outputs.id:''
  }
}

// Azure OpenAI

module openAi 'core/ai/aiservices.bicep' = {
  name: 'openai'
  scope: resourceGroup
  params: {
    name: _openAiServiceName
    location: location
    aiServicesReuse : _azureReuseConfig.aoaiReuse
    existingAiServicesResourceGroupName : _azureReuseConfig.existingAoaiResourceGroupName
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'
    tags: tags
    sku: {
      name: 'S0' 
    }
    secretsNames: { 
      secretName01: 'azureOpenAIKey'
    }
    keyVaultName: keyVault.outputs.name    
    deployments: [
      {
        name: _chatGptDeploymentName
        model: {
          format: 'OpenAI'
          name: _chatGptModelName
          version: _chatGptModelVersion
        }
        sku: {
          name: _chatGptModelDeploymentType
          capacity: _chatGptDeploymentCapacity
        }
      }
      {
        name: _embeddingsDeploymentName
        model: {
          format: 'OpenAI'
          name: _embeddingsModelName
          version: _embeddingsModelVersion
        }
        sku: {
          name: 'Standard'
          capacity: _embeddingsDeploymentCapacity
        }
      }      
    ]
  }
}

module openAiPe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'openAiPe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureOpenAiPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.aiSubId:''
    serviceId: openAi.outputs.id
    groupIds: ['account']
    dnsZoneId: _networkIsolation?openaiDnsZone.outputs.id:''
  }
}

// AI Search

module searchService 'core/search/search-services.bicep' = {
  name: 'search-service'
  scope: resourceGroup
  params: {
    name: _searchServiceName
    location: location
    aiSearchReuse : _azureReuseConfig.aiSearchReuse
    existingAiSearchResourceGroupName : _azureReuseConfig.existingAiSearchResourceGroupName
    secretName: 'azureSearchKey'
    keyVaultName: keyVault.outputs.name
    publicNetworkAccess: _networkIsolation?'Disabled':'Enabled'
    tags: tags
    authOptions: {
      aadOrApiKey: {
        aadAuthFailureMode: 'http401WithBearerChallenge'
      }
    }
    sku: {
      name: _searchServiceSkuName
    }
    semanticSearch: 'free'
  }
}

module searchAzureOpenAIPrivatelink 'core/search/search-private-link.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'searchAzureOpenAIPrivatelink'
  scope: resourceGroup
  dependsOn: [
    openAi, openAiPe
  ] 
  params: {
   name: '${_searchServiceName}-aoailink'
   searchName: searchService.outputs.name
   resourceId: openAi.outputs.id
    groupId: 'openai_account'
  }
}

module searchStoragePrivatelink 'core/search/search-private-link.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'searchStoragePrivatelink'
  scope: resourceGroup
  dependsOn: [
    searchAzureOpenAIPrivatelink, storage, storagepe
  ]  
  params: {
   name: '${_searchServiceName}-storagelink'
   searchName: searchService.outputs.name
   resourceId: storage.outputs.id
   groupId: 'blob'
  }
}

module searchFuncAppPrivatelink 'core/search/search-private-link.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'searchFuncAppPrivatelink'
  scope: resourceGroup
  dependsOn: [
    searchStoragePrivatelink, dataIngestion, ingestionPe
  ]  
  params: {
   name: '${_searchServiceName}-funcapplink'
   searchName: searchService.outputs.name
   resourceId: dataIngestion.outputs.id
    groupId: 'sites'
  }
}

module searchPe './core/network/private-endpoint.bicep' = if (_networkIsolation && !_vnetReuse) {
  name: 'searchPe'
  scope: resourceGroup
  params: {
    location: location
    name: _azureSearchPe
    tags: tags
    subnetId: _networkIsolation?vnet.outputs.aiSubId:''
    serviceId: searchService.outputs.id
    groupIds: ['searchService']
    dnsZoneId: _networkIsolation?searchDnsZone.outputs.id:''
  }
}

module searchStorageAccess './core/security/blobstorage-contributor-access.bicep' = {
  name: 'search-blobstorage-access'
  scope: az.resourceGroup(_storageAccountResourceGroupName)
  params: {
    resourceName: storage.outputs.name
    principalId: searchService.outputs.principalId
  }
}

module searchOaiAccess './core/security/openai-access.bicep' = {
  name: 'search-openai-access'
  scope: az.resourceGroup(_openAiResourceGroupName)
  params: {
    principalId: searchService.outputs.principalId
    resourceName: openAi.outputs.name
  }
} 

module searchIngestionAccess './core/security/functions-access.bicep' = {
  name: 'search-function-access'
  scope: az.resourceGroup(_dataIngestionFunctionAppResourceGroupName)
  params: {
    resourceName: dataIngestion.outputs.name
    principalId: searchService.outputs.principalId
  }
}

// Load Testing

module loadtesting './core/loadtesting/loadtesting.bicep' = if (_provisionLoadTesting){
  name: _loadtestingName
  scope: resourceGroup
  params: {
    name: _loadtestingName
    location: location
    tags: tags
  }
}

module loadtestingKeyVaultAccess './core/security/keyvault-access.bicep' = if (_provisionLoadTesting){
  name: 'loadtesting-keyvault-access'
  scope: az.resourceGroup(_keyVaultResourceGroupName)
  params: {
    resourceName: keyVault.outputs.name
    principalId: _provisionLoadTesting ? loadtesting.outputs.id : ''
  }
} 

/////////////////////////////////////////////////////////////////////////////
// TEMPLATE OUTPUTS
/////////////////////////////////////////////////////////////////////////////

// This section sets certain environment variables as outputs. These outputs can be utilized by post-provisioning/deployment scripts and CI/CD pipelines.
// This approach enables the reconstruction of the .env file from a deployment object on Azure using the environment name, subscription, and location.
// Without this setup, any custom selection would be lost when executing `azd env refresh` from a different machine.

output AZURE_AI_SUBNET_NAME string = _aiSubnetName
output AZURE_AI_SUBNET_PREFIX string = _aiSubnetPrefix
output AZURE_APP_INSIGHTS_NAME string = _appInsightsName
output AZURE_APP_INT_SUBNET_NAME string = _appIntSubnetName
output AZURE_APP_INT_SUBNET_PREFIX string = _appIntSubnetPrefix
output AZURE_APP_SERVICE_NAME string = _appServiceName
output AZURE_APP_SERVICE_PLAN_NAME string = _appServicePlanName
output AZURE_APP_SERVICES_SUBNET_NAME string = _appServicesSubnetName
output AZURE_APP_SERVICES_SUBNET_PREFIX string = _appServicesSubnetPrefix
output AZURE_BASTION_KV_NAME string = _bastionKvName
output AZURE_BASTION_SUBNET_NAME string = _bastionSubnetName
output AZURE_BASTION_SUBNET_PREFIX string = _bastionSubnetPrefix
output AZURE_CHAT_GPT_DEPLOYMENT_CAPACITY int = _chatGptDeploymentCapacity
output AZURE_CHAT_GPT_DEPLOYMENT_NAME string = _chatGptDeploymentName
output AZURE_CHAT_GPT_MODEL_NAME string = _chatGptModelName
output AZURE_CHAT_GPT_MODEL_VERSION string = _chatGptModelVersion
output AZURE_AI_SERVICES_NAME string = _aiServicesName
output AZURE_AI_SERVICES_PE string = _azureAiServicesPe
output AZURE_DB_ACCOUNT_PE string = _azureDbAccountPe
output AZURE_DATA_INGEST_FUNC_NAME string = _dataIngestionFunctionAppName
output AZURE_DATA_INGEST_FUNC_RG string = _resourceGroupName
output AZURE_DATA_INGESTION_PE string = _azureDataIngestionPe
output AZURE_DATABASE_SUBNET_NAME string = _databaseSubnetName
output AZURE_DATABASE_SUBNET_PREFIX string = _databaseSubnetPrefix
output AZURE_DB_CONFIG object = azureDbConfig
output AZURE_FRONTEND_PE string = _azureFrontendPe
output AZURE_KV_NAME string = _keyVaultName
output AZURE_KEY_VAULT_NAME string = _keyVaultName
output AZURE_KEYVAULT_PE string = _azureKeyvaultPe
output AZURE_LOAD_TESTING_NAME string = _loadtestingName
output AZURE_NETWORK_ISOLATION bool = _networkIsolation
output AZURE_OPEN_AI_PE string = _azureOpenAiPe
output AZURE_OPENAI_SERVICE_NAME string = _openAiServiceName
output AZURE_ORCHESTRATOR_FUNC_NAME string = _orchestratorFunctionAppName
output AZURE_ORCHESTRATOR_FUNC_RG string = _resourceGroupName
output AZURE_ORCHESTRATOR_MESSAGES_LANGUAGE string = _orchestratorMessagesLanguage
output AZURE_ORCHESTRATOR_PE string = _azureOrchestratorPe
output AZURE_RESOURCE_GROUP_NAME string = _resourceGroupName
output AZURE_RETRIEVAL_APPROACH string = _retrievalApproach
output AZURE_REUSE_CONFIG object = azureReuseConfig
output AZURE_SEARCH_ANALYZER_NAME string = _searchAnalyzerName
output AZURE_SEARCH_INDEX string = _searchIndex
output AZURE_SEARCH_PE string = _azureSearchPe
output AZURE_SEARCH_PRINCIPAL_ID string = searchService.outputs.principalId
output AZURE_SEARCH_SERVICE_NAME string = _searchServiceName
output AZURE_SPEECH_RECOGNITION_LANGUAGE string = _speechRecognitionLanguage
output AZURE_SPEECH_SYNTHESIS_LANGUAGE string = _speechSynthesisLanguage
output AZURE_SPEECH_SYNTHESIS_VOICE_NAME string = _speechSynthesisVoiceName
output AZURE_STORAGE_ACCOUNT_PE string = _azureStorageAccountPe
output AZURE_STORAGE_ACCOUNT_NAME string = _storageAccountName 
output AZURE_STORAGE_CONTAINER_NAME string = _storageContainerName
output AZURE_SUBSCRIPTION_ID string = subscription().subscriptionId
output AZURE_TENANT_ID string = tenant().tenantId
output AZURE_USE_SEMANTIC_RERANKING bool = _useSemanticReranking
output AZURE_VM_DEPLOY_VM bool = _deployVM
output AZURE_VM_KV_NAME string = _keyVaultName
output AZURE_VM_KV_SEC_NAME string = _networkIsolation ? _vmKeyVaultSecName : ''
output AZURE_VM_NAME string = _networkIsolation ? _ztVmName : ''
output AZURE_VM_USER_NAME string = _networkIsolation ? _vmUserName : ''
output AZURE_VNET_ADDRESS string = _vnetAddress
output AZURE_VNET_NAME string = _vnetName
output AZURE_ZERO_TRUST string = _networkIsolation ? 'TRUE' : 'FALSE'
output AZURE_SEARCH_USE_MIS bool = _azureSearchUseMIS
